package net.hn.hnms.biz.intelligence.config;

import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.TemporalAmount;

import static net.hn.hnms.sys.common.core.jackson.DateTimeConverter.*;
import static org.joda.time.DateTimeConstants.MINUTES_PER_HOUR;
import static org.joda.time.DateTimeConstants.SECONDS_PER_MINUTE;

/**
 * 时间颗粒
 *
 * @author jiangmf
 */
public enum Granularity {
    /**
     * 原始数据的最小颗粒 (一分钟)
     */
    DIRECT_ONE_MINUTE,
    /**
     * 原始数据的最小颗粒 (五分钟)
     */
    DIRECT_FIVE_MINUTES,
    /**
     * 每一分钟
     */
    PER_ONE_MINUTE,
    /**
     * 每五分钟
     */
    PER_FIVE_MINUTES,
    /**
     * 每小时
     */
    PER_HOUR,
    /**
     * 每日
     */
    PER_DAY,
    /**
     * 每次开关量变化
     */
    PER_SWITCH_CHANGE,

    SWITCH_CHANGE,

    /**
     * 四小时
     */
    FOUR_HOUR;

    /**
     * 时间分组方法
     *
     * @param x
     * @return
     */
    public String group(LocalDateTime x) {
        LocalDateTime adjustTime = sanitize(x);
        switch (this) {
            case PER_DAY:
                return adjustTime.atZone(ZoneId.systemDefault()).format(DATE_FORMATTER_WITHOUT_TIMEZONE);
            case PER_HOUR:
                return adjustTime.atZone(ZoneId.systemDefault()).format(DATE_HOUR_FORMATTER_WITH_TIMEZONE);
            case PER_FIVE_MINUTES:
            case PER_ONE_MINUTE:
            case DIRECT_FIVE_MINUTES:
            case DIRECT_ONE_MINUTE:
            default:
                return adjustTime.atZone(ZoneId.systemDefault()).format(DATE_TIME_FORMATTER_WITH_TIMEZONE);
        }
    }

    /**
     * 时间分组方法
     *
     * @param x
     * @return
     */
    public String group(Instant x) {
        return group(LocalDateTime.ofInstant(x, ZoneId.systemDefault()));
    }

    /**
     * 时间分组方法
     *
     * @param x
     * @return
     */
    public String group(LocalDateTime x, LocalDateTime startTime, LocalDateTime endTime) {
        if (this == PER_SWITCH_CHANGE) {
            LocalDateTime adjustTime = sanitize(x, startTime);
            return adjustTime.atZone(ZoneId.systemDefault()).format(DATE_TIME_FORMATTER_WITH_TIMEZONE);
        } else {
            return group(x);
        }
    }

    /**
     * 时间分组方法
     *
     * @param x
     * @return
     */
    public String group(Instant x, LocalDateTime startTime, LocalDateTime endTime) {
        return group(LocalDateTime.ofInstant(x, ZoneId.systemDefault()), startTime, endTime);
    }

    /**
     * 时间纠正方法
     */
    public LocalDateTime sanitize(LocalDateTime x) {
        switch (this) {
            case PER_DAY:
                return x.withHour(0).withMinute(0).withSecond(0);
            case PER_HOUR:
                int halfOfHour = MINUTES_PER_HOUR / 2;
                if (x.getMinute() < halfOfHour) {
                    // 前半小时，舍弃分钟数
                    return x.withSecond(0);
                } else {
                    // 后半小时，算入下一小时
                    return x.withSecond(0).plusMinutes(1);
                }
            case PER_FIVE_MINUTES:
                int minute = Math.round(x.getMinute() / 5.0f) * 5;
                if (minute < MINUTES_PER_HOUR) {
                    // 计算分钟数不超过60的情况，使用计算分钟
                    return x.withMinute(minute).withSecond(0);
                } else {
                    // 计算分钟数恰巧为60的情况，舍弃计算分钟，上加一小时
                    return x.withMinute(0).withSecond(0).plusHours(1);
                }
            case PER_ONE_MINUTE:
                int halfOfMinute = SECONDS_PER_MINUTE / 2;
                if (x.getSecond() < halfOfMinute) {
                    // 前半分钟，舍弃秒数
                    return x.withSecond(0);
                } else {
                    // 后半分钟，算入下一分钟
                    return x.withSecond(0).plusMinutes(1);
                }
            case DIRECT_FIVE_MINUTES:
            case DIRECT_ONE_MINUTE:
            default:
                return x;
        }
    }

    /**
     * 时间纠正方法
     */
    public LocalDateTime sanitize(LocalDateTime x, LocalDateTime startTime) {
        if (this == PER_SWITCH_CHANGE && x.isBefore(startTime)) {
            return startTime;
        } else {
            return sanitize(x);
        }

    }

    /**
     * 时间纠正方法，带基准值，算法分析用
     */
    public LocalDateTime sanitizeWith(LocalDateTime x, LocalDateTime startTime, TemporalAmount deltaTime) {
        switch (this) {
            case PER_ONE_MINUTE:
                final Duration between1 = Duration.between(x, startTime.plus(deltaTime));
                if (Math.abs(between1.getSeconds()) < SECONDS_PER_MINUTE) {
                    return x;
                } else {
                    return x.plusMinutes(1);
                }
            case PER_FIVE_MINUTES:
                final Duration between2 = Duration.between(x, startTime.plus(deltaTime));
                if (Math.abs(between2.toMinutes()) < 5L) {
                    return x;
                } else {
                    return x.plusMinutes(5);
                }
            default:
                return sanitize(x, startTime);
        }
    }

    /**
     * 时间颗粒的递增方法,plus之前需要对入参做时间纠正
     *
     * @param localDateTime
     * @return
     */
    public LocalDateTime plus(LocalDateTime localDateTime) {

        switch (this) {
            case PER_DAY:
                return localDateTime.plusDays(1);
            case PER_HOUR:
                return localDateTime.plusHours(1);
            case PER_FIVE_MINUTES:
                return localDateTime.plusMinutes(5);
            case PER_ONE_MINUTE:
                return localDateTime.plusMinutes(1);
            case DIRECT_FIVE_MINUTES:
            case DIRECT_ONE_MINUTE:
            default:
                throw new UnsupportedOperationException("非法的时间颗粒范围");
        }

    }
}
